package com.hoangkien.config;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import com.hoangkien.network.Command;
import com.hoangkien.network.IProtocol;
import com.hoangkien.network.IRunable;
import com.hoangkien.network.JSONProtocol;
import com.hoangkien.network.task.TaskGetListPromotions;
import com.hoangkien.util.Listener;
import android.content.Context;
import android.os.Process;

public class Core implements IConfig,ICore, Runnable{
	private static Core instance = null;
	private Context mContext;
	private Thread mThread;
	private IProtocol protocol;
	private boolean coreFlag = false;
	private boolean connected = true;
	private byte[] connectionLock = new byte[0];
	public static AtomicInteger sequencing = new AtomicInteger(0);
	private BlockingQueue<Command> mCommands = new PriorityBlockingQueue<Command>();
	public int sleepTime = 10 * 1000;
	private static Set<Listener> mListener = new CopyOnWriteArraySet<Listener>();
	public static final int PRIORITY_MEDIUM = 3;
	private String authKey = UNINITIALIZED_STRING;
//	private BMSSharedPreferences mPreferences;
	
	public Core(Context context) {
		this.mContext = context;
		this.protocol = new JSONProtocol(context, this);
		mThread = new Thread(this);
		mThread.setName("CORE");
		mThread.start();
	}
	
	public synchronized static Core getInstance(Context context) {
		if (instance == null) {
			instance = new Core(context);
		}
		return instance;
	}

	@Override
	public void run() {
		Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
		coreFlag = true;
		while (coreFlag) {
			String commandDescription = null;
			try {
				final Command command = mCommands.take();
				if (!connected) {
					if (command != null) {
						if (command.sendByUI) {
							command.runable.timeoutCallback();
							continue;
						} else {
							mCommands.add(command);
						}
					}
					synchronized (connectionLock) {
						connectionLock.wait(NETWORK_FAIL_WAIT);
						continue;
					}
				}

				if (command != null) {
					commandDescription = command.action;
					try {
						command.runable.run();
						resetSleepTime();
					} catch (Exception e) {
						if (command.sendByUI) {
							command.runable.timeoutCallback();
						} else {
							if (command.retryTime > 0) {
								command.retryTime--;
								new Thread() {
									@Override
									public void run() {
										try {
											sleep(increaseSleepTime());
											mCommands.put(command);
										} catch (InterruptedException e) {
										}
									}

								}.start();
							}
						}
						e.printStackTrace();
					}
					for (Listener l : copyAndReconstructListeners(command.listener)) {
						l.commandExecutionComplete(mCommands.size() > 0);
					}
				}
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private void resetSleepTime() {
		sleepTime = 10 * 1000;
	}
	
	private int increaseSleepTime() {
		if (sleepTime == 10 * 1000) { // 30 seconds
			sleepTime = 30 * 1000;
		} else if (sleepTime == 30 * 1000) { // 3 minutes
			sleepTime = 3 * 60 * 1000;
		} else if (sleepTime == 3 * 60 * 1000) { // 5 minutes
			sleepTime = 5 * 60 * 1000;
		}
		return sleepTime;
	}
	
	@Override
	public Set<Listener> copyAndReconstructListeners(Listener listener) {
		if (listener == null) {
			return mListener;
		}
		Set<Listener> listeners = new HashSet<Listener>(mListener);
		listeners.add(listener);
		return listeners;
	}

	@Override
	public String getAuthKey() {
		return null;
//		return (authKey.equals(UNINITIALIZED_STRING)) ? mPreferences.getUserAuthKey(mContext) : authKey;
	}

	@Override
	public Context getContext() {
		return mContext;
	}

	@Override
	public IProtocol getProtocol() {
		return protocol;
	}
	
	public void addListener(Listener listener) {
		if (listener != null) {
			mListener.add(listener);
		}
	}

	public void removeListener(Listener listener) {
		mListener.remove(listener);
	}
	
	@Override
	public void stopThreads() {
		mCommands.clear();
		restartCore();
	}

	@Override
	public void stopCoreAndDestroy() {
		this.stopThreads();
		mThread.interrupt();
		mThread = null;
	}
	
	private void restartCore() {
		coreFlag = false;
		mThread = new Thread(this);
		mThread.start();
	}
	
	public void put(Listener listener, String action, int priority, IRunable runable, boolean sendByUI) {
		putCommand(mCommands, action, listener, runable, priority, sendByUI, Command.DEFAULT_RETRY_TIMES);
	}
	
	private void putCommand(BlockingQueue<Command> queue, String action,
			Listener listener, IRunable runnable, int priority,
			boolean sendByUI, int retryTimes) {
		int retries = 10;
		Exception e = null;
		while (retries-- > 0) {
			try {

				Command command = new Command(runnable, listener, action,
						priority, sendByUI);
				queue.put(command);
				return;
			} catch (InterruptedException ie) {
				try {
					Thread.sleep(IConfig.PUT_COMMAND_RETRY_INTERVAL);
				} catch (InterruptedException ne) {
				}
				e = ie;
			}
		}
		throw new Error(e);
	}
	
	
	
	@Override
	public int taskGetListPromotions(int pageLimit, int pageNumber, Listener listener) {
		TaskGetListPromotions task = new TaskGetListPromotions(listener,instance, pageLimit, pageNumber);
		put(listener, task.getClass().getName(), PRIORITY_MEDIUM, task, true);
		return 0;
	}
}
